package pl.edu.agh.student.janikhankus.earley;

import java.util.ArrayList;
import java.util.TreeSet;

/**
 * Class making parse trees basing on Earley's situations sets
 * @author tom
 *
 */
public class Forest {
	boolean debug = false;
	boolean makingTreeDebug = false;

	public Forest() {
	}

	/**
	 * Creates parse trees
	 * @param node
	 * @param parse_sets
	 * @param currentPath
	 * @return
	 */
	public ArrayList<ArrayList<MyTree>> forest(Precedents node,
			ArrayList<OrderedDict> parse_sets, TreeSet<Precedents> currentPath) {

		if (currentPath == null) {
			currentPath = new TreeSet<Precedents>();
			currentPath.add(new Precedents(null, null));
		}
		if (currentPath.contains(node)) {
			return null;
		}
		currentPath.add(node);
		ParseProduction predecessor = node.getPredecessor();
		ParseProduction reduction = node.getReduction();
		ArrayList<ArrayList<MyTree>> base = new ArrayList<ArrayList<MyTree>>();
		ArrayList<ArrayList<MyTree>> result;
		// czy po prawej stronie nie terminal
		char c = 'A';
		if (reduction != null) {
			if (reduction instanceof FinalProduction) {
				c = 'a';
			} else {
				c = reduction.getRight().get(0).getName().charAt(0);
			}

		}
		if (Character.toLowerCase(c) == c
				|| reduction.getRight().get(0).getName().equals("Epsylon")) {
			if (debug) {
				Earley.printParseProduction(predecessor);
				Earley.printParseProduction(reduction);
			}
			base.add(makeLeaf(predecessor.getRight().get(predecessor.getDot())
					.getName(), reduction.getProbability(), reduction));
		} else {
			if (debug) {
				Earley.printParseProduction(predecessor);
				Earley.printParseProduction(reduction);
			}
			int i = reduction.getI();
			String left = reduction.getLeft().getName();
			TreeSet<Precedents> iterNodesList = parse_sets.get(i).d
					.get(reduction);
			if (iterNodesList != null) {
				for (Precedents rNode : iterNodesList) {
					if (debug && rNode.getPredecessor() == null
							&& rNode.getReduction() == null) {
						System.out.print("znalazlem, rNode:");
						Earley.printParseProduction(reduction);
					}
					for (ArrayList<MyTree> kids : forest(rNode, parse_sets,
							currentPath)) {
						base.add(makeNode(left, reduction.getProbability(),
								kids));
					}
				}
			}
		}
		result = base;

		ArrayList<ArrayList<MyTree>> extended = new ArrayList<ArrayList<MyTree>>();
		int i = predecessor.getI();
		ArrayList<MyTree> tmpTreeList;
		TreeSet<Precedents> tmpPrecList = parse_sets.get(i).d.get(predecessor);
		if (tmpPrecList != null) {
			for (Precedents pNode : tmpPrecList) {
				if (!(pNode.getPredecessor() == null && pNode.getReduction() == null)) {
					if (debug && pNode.getPredecessor() == null
							&& pNode.getReduction() == null) {
						System.out.print("znalazlem, pNode:");
						Earley.printParseProduction(predecessor);
					}
					ArrayList<ArrayList<MyTree>> variants = forest(pNode,
							parse_sets, currentPath);
					if (variants != null) {
						if (variants.size() != 0) {
							for (ArrayList<MyTree> vList : variants) {
								for (ArrayList<MyTree> bList : base) {
									tmpTreeList = new ArrayList<MyTree>();
									for (MyTree vTree : vList) {
										tmpTreeList.add(vTree);
									}
									for (MyTree bTree : bList) {
										tmpTreeList.add(bTree);
									}
									extended.add(tmpTreeList);
								}
							}
							result = extended;
						}
					}
				}
			}
		}
		currentPath.remove(node);
		return result;

	}

	ArrayList<MyTree> makeNode(String symbol, double prob,
			ArrayList<MyTree> list) {
		if (makingTreeDebug)
			System.out.println("Adding node");
		MyTree tree = new MyTree(symbol, prob);
		for (MyTree treeIter : list) {
			tree.addChild(treeIter);
		}

		ArrayList<MyTree> tmpList = new ArrayList<MyTree>();
		tmpList.add(tree);
		return tmpList;
	}

	ArrayList<MyTree> makeLeaf(String symbol, double prob,
			ParseProduction production) {
		if (makingTreeDebug)
			System.out.println("Adding leaf");
		ArrayList<MyTree> tmpList = new ArrayList<MyTree>();
		MyTree tree = new MyTree(symbol, prob);
		if (production instanceof FinalProduction) {
			FinalProduction finalProd = (FinalProduction) production;
			tree.addChild(new MyTree(finalProd.getTerminal(), -1));
		} else
			tree.addChild(new MyTree(production.getRight().get(0).getName(), -1));
		tmpList.add(tree);
		return tmpList;
	}
}
